﻿using System;
using System.Collections.Concurrent;
using System.Linq;
using System.Threading.Tasks;
using BASIC_CORE.Domain.Caching.Repositories;

namespace BASIC_CORE.Services.Redis
{
    /// <summary>
    /// 服务锁
    /// </summary>
    public class Procedure
    {
        private readonly IDistributeLockCacheRepository _distributeLockCacheRepository;

        public Procedure(IDistributeLockCacheRepository distributeLockCacheRepository)
        {
            _distributeLockCacheRepository = distributeLockCacheRepository;
        }

        #region 单机锁

        /// <summary>
        /// 异步：单机锁 过程执行锁定（在上一次请求未处理完毕，该方法不处理当前的请求,视当前请求为繁忙）
        /// </summary>
        /// <typeparam name="T">泛型</typeparam>
        /// <param name="isLocked">锁定因子</param>
        /// <param name="busyCb">繁忙回调</param>
        /// <param name="methodSubjectCb">方法过程</param>
        /// <returns></returns>
        public Task<T> LockAsync<T>(ref bool isLocked, Func<T> busyCb, Func<Task<T>> methodSubjectCb)
        {
            if (isLocked)
            {
                //过程繁忙回调
                return Task.FromResult(busyCb.Invoke());
            }
            else
            {
                try
                {
                    isLocked = true;
                    return methodSubjectCb.Invoke();
                }
                catch (Exception)
                {
                    isLocked = false;
                    return Task.FromResult(default(T));
                }
                finally
                {
                    //解除过程锁定
                    isLocked = false;
                }
            }
        }


        /// <summary>
        /// 异步：单机锁 过程执行锁定（相同的时间并且相同的唯一标识提交的请求会被锁定，即同一时刻该方法不处理相同标识的请求，直到该标识被释放才会处理，但处理有多个不同标识的请求）
        /// </summary>
        /// <typeparam name="T">泛型</typeparam>
        /// <param name="lockedList">被锁的的标识列表</param>
        /// <param name="lockId">本次锁定的标识</param>
        /// <param name="busyCb">过程繁忙回调方法</param>
        /// <param name="methodSubjectCb">方法过程方法</param>
        /// <returns></returns>
        public Task<T> LockAsync<T>(ref ConcurrentBag<string> lockedList, string lockId, Func<T> busyCb, Func<Task<T>> methodSubjectCb)
        {
            if (lockedList == null && lockedList.Contains(lockId))
            {
                //过程繁忙回调
                return Task.FromResult(busyCb.Invoke());
            }
            else
            {
                try
                {
                    lockedList.Add(lockId);
                    return methodSubjectCb.Invoke();
                }
                catch (Exception)
                {
                    lockedList.TryTake(out lockId);
                    return Task.FromResult(default(T));
                }
                finally
                {
                    //解除过程锁定
                    lockedList.TryTake(out lockId);
                }
            }
        }

        #endregion 单机锁

        #region 基于Redis分布式锁

        /// <summary>
        /// 异步：分布式过程执行锁定（相同的时间并且相同的唯一标识提交的请求会被锁定，即同一时刻该方法不处理相同标识的请求，直到该标识被释放才会处理，但处理有多个不同标识的请求）
        /// </summary>
        /// <typeparam name="T">泛型</typeparam>
        /// <param name="lockId">本次锁定的标识（一般是方法名称加用户标识，例如:UserEventService.RegisterUser18317171869）</param>
        /// <param name="busyCb">过程繁忙回调方法</param>
        /// <param name="methodSubjectCb">方法过程方法</param>
        /// <returns></returns>
        public Task<T> DistributeLockAsync<T>(string lockId, Func<T> busyCb, Func<Task<T>> methodSubjectCb)
        {
            string key = "DistributeLockAsyncLocked";
            lock (key)
            {
                var isExist = _distributeLockCacheRepository.IsExistLockedId(lockId);
                if (isExist)
                {
                    //过程繁忙回调
                    return Task.FromResult(busyCb.Invoke());
                }
                else
                {
                    try
                    {
                        _distributeLockCacheRepository.SetLockedId(lockId);
                        return methodSubjectCb.Invoke();
                    }
                    catch
                    {
                        _distributeLockCacheRepository.RemoveLockedId(lockId);
                        return Task.FromResult(default(T));
                    }
                    finally
                    {
                        //解除过程锁定
                        _distributeLockCacheRepository.RemoveLockedId(lockId);
                    }
                }
            }
        }

        #endregion 基于Redis分布式锁
    }
}
